package com.mhz.widgets.client.multicombo;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.CheckboxCell;
import com.google.gwt.cell.client.CompositeCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.HasCell;
import com.google.gwt.cell.client.SafeHtmlCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.regexp.shared.MatchResult;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.cellview.client.CellList;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.DefaultSelectionEventManager;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.MultiSelectionModel;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.mhz.widgets.shared.MultiComboItem;

public class MultiComboList extends Composite {

	private static ViewWidgetUiBinder uiBinder = GWT.create(ViewWidgetUiBinder.class);

	interface ViewWidgetUiBinder extends UiBinder<Widget, MultiComboList> {
	}

	@UiField
	PopupPanel popupPanel;

	@UiField
	TextBox comboFilter;

	@UiField
	ScrollPanel scrollPanel;

	CellList<MultiComboItem> cellList;

	ListDataProvider<MultiComboItem> dataProvider;

	MultiComboSelectHandler selectHandler;

	public MultiComboList() {
		// Init uiBinder
		initWidget(uiBinder.createAndBindUi(this));

		popupPanel.setAutoHideEnabled(true);
		popupPanel.setPopupPosition(this.getAbsoluteLeft(), this.getAbsoluteTop());

		scrollPanel.setAlwaysShowScrollBars(false);

		// Build the selection model
		final MultiSelectionModel<MultiComboItem> selectionModel = new MultiSelectionModel<MultiComboItem>(MultiComboItem.KEY_PROVIDER);
		selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
			@Override
			public void onSelectionChange(SelectionChangeEvent event) {
				if (selectHandler != null) {
					selectHandler.onSelectItems(selectionModel.getSelectedSet());
				}
			}
		});

		// Build the composite cell that includes a checkbox.
		List<HasCell<MultiComboItem, ?>> hasCells = new ArrayList<HasCell<MultiComboItem, ?>>();
		hasCells.add(new HasCell<MultiComboItem, Boolean>() {

			private final CheckboxCell cell = new CheckboxCell();

			@Override
			public Cell<Boolean> getCell() {
				return cell;
			}

			@Override
			public FieldUpdater<MultiComboItem, Boolean> getFieldUpdater() {
				return null;
			}

			@Override
			public Boolean getValue(MultiComboItem object) {
				return selectionModel.isSelected(object);
			}

		});

		hasCells.add(new HasCell<MultiComboItem, SafeHtml>() {

			private final SafeHtmlCell cell = new SafeHtmlCell();

			@Override
			public Cell<SafeHtml> getCell() {
				return cell;
			}

			@Override
			public FieldUpdater<MultiComboItem, SafeHtml> getFieldUpdater() {
				return null;
			}

			@Override
			@SuppressWarnings("serial")
			public SafeHtml getValue(final MultiComboItem object) {
				return new SafeHtml() {
					@Override
					public String asString() {
						return object.getFormatedValue();
					}
				};
			}

		});

		CompositeCell<MultiComboItem> compoCell = new CompositeCell<MultiComboItem>(hasCells) {
			@Override
			public void render(Context context, MultiComboItem value, SafeHtmlBuilder sb) {
				super.render(context, value, sb);
			}

			@Override
			protected <X> void render(Context context, MultiComboItem value, SafeHtmlBuilder sb, HasCell<MultiComboItem, X> hasCell) {
				if (value.isVisible()) {
					Cell<X> cell = hasCell.getCell();
					cell.render(context, hasCell.getValue(value), sb);
				}
			}

			@Override
			protected Element getContainerElement(Element parent) {
				return parent;
			}

			@Override
			public boolean resetFocus(Context context, Element parent, MultiComboItem value) {
				return true;
			}

		};

		// Create the Checkbox manager
		DefaultSelectionEventManager<MultiComboItem> selectionManager = DefaultSelectionEventManager.createCheckboxManager();
		// Build the cell list
		cellList = new CellList<MultiComboItem>(compoCell);
		cellList.setSelectionModel(selectionModel, selectionManager);
		// Build the data provider
		dataProvider = new ListDataProvider<MultiComboItem>();
		dataProvider.addDataDisplay(cellList);
		// Add cell list to the panel
		scrollPanel.add(cellList);

		popupPanel.addCloseHandler(new CloseHandler<PopupPanel>() {

			@Override
			public void onClose(CloseEvent<PopupPanel> event) {
				display = false;
			}
		});

	}

	public void setListContent(List<MultiComboItem> listContent) {
		dataProvider.setList(listContent);
	}

	public ScrollPanel getScrollPanel() {
		return scrollPanel;
	}

	public CellList<MultiComboItem> getCellList() {
		return cellList;
	}

	@SuppressWarnings("unchecked")
	public List<MultiComboItem> getSelectedItems() {
		MultiSelectionModel<MultiComboItem> selectionModel = (MultiSelectionModel<MultiComboItem>) cellList.getSelectionModel();
		List<MultiComboItem> selected = new ArrayList<MultiComboItem>(selectionModel.getSelectedSet());
		return selected;
	}

	@SuppressWarnings("unchecked")
	public void setSelectedItems(List<String> selectedIds) {
		MultiSelectionModel<MultiComboItem> selectionModel = (MultiSelectionModel<MultiComboItem>) cellList.getSelectionModel();
		if (selectedIds == null || selectedIds.isEmpty()) {
			selectionModel.clear();
		} else {
			for (MultiComboItem pairValue : dataProvider.getList()) {
				if (selectedIds.contains(pairValue.getCode())) {
					selectionModel.setSelected(pairValue, true);
				} else {
					selectionModel.setSelected(pairValue, false);
				}
			}
			for (MultiComboItem pairValue : selectionModel.getSelectedSet()) {
				if (selectedIds.contains(pairValue.getCode())) {
					selectionModel.setSelected(pairValue, true);
				} else {
					selectionModel.setSelected(pairValue, false);
				}
			}
		}
		dataProvider.refresh();
	}

	@UiHandler("comboFilter")
	void filterComboItems(KeyUpEvent event) {
		filterItems(comboFilter.getText());
	}

	public void filterItems(String filter) {
		if (filter != null) {
			// Replace all special caracters
			filter = filter.replaceAll("(?:[\\\\\\[\\]\\(\\)\\{\\}\\-\\.\\*\\+\\?\\|\\^\\$])", "");
			// Apply the filter to items
			for (MultiComboItem item : dataProvider.getList()) {
				item.setFormatedValue(item.getValue());
				RegExp p = RegExp.compile(filter, "i");
				if (p.test(item.getValue())) {
					item.setVisible(true);
					MatchResult result = p.exec(item.getValue());
					item.setFormatedValue(item.getFormatedValue().replaceFirst(result.getGroup(0), "<b>" + result.getGroup(0) + "</b>"));
				} else {
					item.setVisible(false);
				}
			}
		}
		dataProvider.refresh();
	}

	/** Add the selection handler */
	public void addSelectHandler(MultiComboSelectHandler selectHandler) {
		this.selectHandler = selectHandler;
	}

	/** Interface used manage items selection */
	public interface MultiComboSelectHandler {
		void onSelectItems(Set<MultiComboItem> items);
	}

	private boolean display;

	public void show() {
		display = !display;
		setVisible(display);
		if (display) {
			popupPanel.setPopupPosition(this.getAbsoluteLeft(), this.getAbsoluteTop());
			popupPanel.show();
		}
	}

	public PopupPanel getPopupPanel() {
		return popupPanel;
	}

}
